﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using K9Nano.Caching;
using K9Nano.Share.Extensions;
using K9Nano.Share.Reflection;
using Microsoft.Extensions.Caching.Memory;

namespace Microsoft.Extensions.DependencyInjection
{
    public class DefaultMemoryCache : ICache
    {
        protected readonly IMemoryCache MemoryCache;

        public DefaultMemoryCache(IMemoryCache memoryCache)
        {
            MemoryCache = memoryCache;
        }
        
        public virtual bool Set(string key, object value, int expireSeconds = -1)
        {
            if (expireSeconds > 0)
            {
                MemoryCache.Set(key, value, TimeSpan.FromSeconds(expireSeconds));
            }
            else
            {
                MemoryCache.Set(key, value);
            }

            return true;
        }

        public virtual Task<bool> SetAsync(string key, object value, int expireSeconds = -1, CancellationToken cancellation = default)
        {
            Set(key, value, expireSeconds);
            return Task.FromResult(true);
        }

        public virtual T Get<T>(string key, int refreshSeconds = 0)
        {
            var t = MemoryCache.Get<T>(key);
            if (refreshSeconds > 0)
            {
                Expire(key, refreshSeconds);
            }

            return t;
        }

        public virtual bool TryGet<T>(string key, out T result, int refreshSeconds = 0)
        {
           var r = MemoryCache.TryGetValue(key, out result);
           if (r && refreshSeconds > 0) Expire(key, refreshSeconds);
           return r;
        }

        public virtual Task<T> GetAsync<T>(string key, int refreshSeconds = 0, CancellationToken cancellation = default)
        {
            var t = Get<T>(key, refreshSeconds);
            return Task.FromResult(t);
        }

        public virtual void Remove(params string[] key)
        {
            foreach (var k in key)
            {
                MemoryCache.Remove(k);
            }
        }

        public virtual Task RemoveAsync(string[] key, CancellationToken cancellation = default)
        {
            Remove(key);
            return Task.CompletedTask;
        }

        public virtual Task RemoveAsync(string key, CancellationToken cancellation = default)
        {
            Remove(key);
            return Task.CompletedTask;
        }

        public virtual void RemovePattern(string pattern)
        {
            var keys = FilterKeys(s => s.WildcardMatch(pattern))
                .ToArray();
            this.Remove(keys);
        }

        public virtual Task RemovePatternAsync(string pattern, CancellationToken cancellation = default)
        {
            RemovePattern(pattern);
            return Task.CompletedTask;
        }

        public virtual bool Expire(string key, int seconds)
        {
            using var entry = MemoryCache.CreateEntry(key);
            entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(seconds);
            return true;
        }

        public virtual Task<bool> ExpireAsync(string key, int seconds, CancellationToken cancellation = default)
        {
            Expire(key, seconds);
            return Task.FromResult(true);
        }

        protected virtual IEnumerable<string> FilterKeys(Func<string, bool> predicate)
        {
            // ConcurrentDictionary<object, CacheEntry> _entries
            var entries = (System.Collections.IDictionary)MemoryCache.GetField("_entries");
            return entries.Keys.Cast<string>().Where(predicate);
        }
    }
}